/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.primitives.cadmium;

import java.lang.reflect.Array;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.CodeRunner;
import fr.x9c.cadmium.kernel.Custom;
import fr.x9c.cadmium.kernel.Fail;
import fr.x9c.cadmium.kernel.Primitive;
import fr.x9c.cadmium.kernel.PrimitiveProvider;
import fr.x9c.cadmium.kernel.Value;

/**
 * This class provides primitives related to {@link java.lang.reflect.Array}.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
@PrimitiveProvider
public final class Arrays {

    /**
     * No instance of this class.
     */
    private Arrays() {
    } // end empty constructor

    /**
     * Creates a new array.
     * @param ctxt context
     * @param type array elements type
     * @param size array size
     * @return new array with passed size and array elements type
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_new_instance(final CodeRunner ctxt,
                                                   final Value type,
                                                   final Value size)
        throws Fail.Exception {
        try {
            return Cadmium.createObject(Array.newInstance(Cadmium.decodeType(type),
                                                          size.asLong()));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_new_instance(CodeRunner, Value, Value)'

    /**
     * Creates a new array.
     * @param ctxt context
     * @param type array elements type
     * @param sizes array dimensions
     * @return new array with passed dimensions and array elements type
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_new_instance_dims(final CodeRunner ctxt,
                                                        final Value type,
                                                        final Value sizes)
        throws Fail.Exception {
        try {
            return Cadmium.createObject(Array.newInstance(Cadmium.decodeType(type),
                                                          Cadmium.decodeIntList(sizes)));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_new_instance_dims(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getLength(java.lang.Object)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_length(final CodeRunner ctxt,
                                                 final Value array)
        throws Fail.Exception {
        try {
            return Value.createFromLong(Array.getLength(array.asBlock().asCustom()));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_length(CodeRunner, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#get(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get(final CodeRunner ctxt,
                                          final Value array,
                                          final Value index)
        throws Fail.Exception {
        try {
            return Cadmium.createObject(Array.get(array.asBlock().asCustom(),
                                                  index.asLong()));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getBoolean(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_boolean(final CodeRunner ctxt,
                                                  final Value array,
                                                  final Value index)
        throws Fail.Exception {
        try {
            return Array.getBoolean(array.asBlock().asCustom(),
                                    index.asLong())
                ? Value.TRUE
                : Value.FALSE;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_boolean(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getByte(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_byte(final CodeRunner ctxt,
                                               final Value array,
                                               final Value index)
        throws Fail.Exception {
        try {
            return Value.createFromLong(Array.getByte(array.asBlock().asCustom(),
                                                      index.asLong()));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_byte(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getChar(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_char(final CodeRunner ctxt,
                                               final Value array,
                                               final Value index)
        throws Fail.Exception {
        try {
            return Value.createFromLong(Array.getChar(array.asBlock().asCustom(),
                                                      index.asLong()));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_char(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getDouble(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_double(final CodeRunner ctxt,
                                                 final Value array,
                                                 final Value index)
        throws Fail.Exception {
        try {
            final Block b = Block.createDouble(Array.getDouble(array.asBlock().asCustom(),
                                                               index.asLong()));
            return Value.createFromBlock(b);
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_double(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getFloat(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_float(final CodeRunner ctxt,
                                                final Value array,
                                                final Value index)
        throws Fail.Exception {
        try {
            final Block b = Block.createDouble(Array.getFloat(array.asBlock().asCustom(),
                                                              index.asLong()));
            return Value.createFromBlock(b);
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_float(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getInt(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_int(final CodeRunner ctxt,
                                              final Value array,
                                              final Value index)
        throws Fail.Exception {
        try {
            final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                               Custom.INT_32_OPS);
            b.setInt32(Array.getInt(array.asBlock().asCustom(),
                                    index.asLong()));
            return Value.createFromBlock(b);
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_int(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getLong(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_long(final CodeRunner ctxt,
                                               final Value array,
                                               final Value index)
        throws Fail.Exception {
        try {
            final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                               Custom.INT_64_OPS);
            b.setInt64(Array.getLong(array.asBlock().asCustom(),
                                     index.asLong()));
            return Value.createFromBlock(b);
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_long(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#getShort(java.lang.Object, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_get_short(final CodeRunner ctxt,
                                                final Value array,
                                                final Value index)
        throws Fail.Exception {
        try {
            return Value.createFromLong(Array.getShort(array.asBlock().asCustom(),
                                                       index.asLong()));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_get_short(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#set(java.lang.Object, int, java.lang.Object)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set(final CodeRunner ctxt,
                                          final Value array,
                                          final Value index,
                                          final Value val)
        throws Fail.Exception {
        try {
            Array.set(array.asBlock().asCustom(),
                      index.asLong(),
                      val.asBlock().asCustom());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#setBoolean(java.lang.Object, int, boolean)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set_boolean(final CodeRunner ctxt,
                                                  final Value array,
                                                  final Value index,
                                                  final Value val)
        throws Fail.Exception {
        try {
            Array.setBoolean(array.asBlock().asCustom(),
                             index.asLong(),
                             val == Value.TRUE);
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set_boolean(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#setByte(java.lang.Object, int, byte)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set_byte(final CodeRunner ctxt,
                                               final Value array,
                                               final Value index,
                                               final Value val)
        throws Fail.Exception {
        try {
            Array.setByte(array.asBlock().asCustom(),
                          index.asLong(),
                          (byte) val.asLong());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set_byte(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#setChar(java.lang.Object, int, char)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set_char(final CodeRunner ctxt,
                                               final Value array,
                                               final Value index,
                                               final Value val)
        throws Fail.Exception {
        try {
            Array.setChar(array.asBlock().asCustom(),
                          index.asLong(),
                          (char) val.asLong());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set_char(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#setDouble(java.lang.Object, int, double)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set_double(final CodeRunner ctxt,
                                                 final Value array,
                                                 final Value index,
                                                 final Value val)
        throws Fail.Exception {
        try {
            Array.setDouble(array.asBlock().asCustom(),
                            index.asLong(),
                            val.asBlock().asDouble());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set_double(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#setFloat(java.lang.Object, int, float)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set_float(final CodeRunner ctxt,
                                                final Value array,
                                                final Value index,
                                                final Value val)
        throws Fail.Exception {
        try {
            Array.setFloat(array.asBlock().asCustom(),
                           index.asLong(),
                           (float) val.asBlock().asDouble());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set_float(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#setInt(java.lang.Object, int, int)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set_int(final CodeRunner ctxt,
                                              final Value array,
                                              final Value index,
                                              final Value val)
        throws Fail.Exception {
        try {
            Array.setInt(array.asBlock().asCustom(),
                         index.asLong(),
                         val.asBlock().asInt32());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set_int(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#setLong(java.lang.Object, int, long)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set_long(final CodeRunner ctxt,
                                               final Value array,
                                               final Value index,
                                               final Value val)
        throws Fail.Exception {
        try {
            Array.setLong(array.asBlock().asCustom(),
                          index.asLong(),
                          val.asBlock().asInt64());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set_long(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Array#setShort(java.lang.Object, int, short)} method.
     * @param ctxt context
     * @param array object to execute method on
     * @param index parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_array_set_short(final CodeRunner ctxt,
                                                final Value array,
                                                final Value index,
                                                final Value val)
        throws Fail.Exception {
        try {
            Array.setShort(array.asBlock().asCustom(),
                           index.asLong(),
                           (short) val.asLong());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_array_set_short(CodeRunner, Value, Value, Value)'

} // end class 'Arrays'
